{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "845f96f8-9e85-4a93-8d44-183aad6e4060",
   "metadata": {},
   "source": [
    "<center><h1><a href=\"https://xinghuo.xfyun.cn/sparkapi?ch=nt_api_m1ophux\">注册链接：https://xinghuo.xfyun.cn/sparkapi?ch=nt_api_m1ophux</a> </h1></center>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0fdf7354-b1e1-45f1-b482-882183589fdb",
   "metadata": {},
   "source": [
    "## 文本对话\n",
    "\n",
    "- 控制台：https://console.xfyun.cn/services/bm35\n",
    "- API文档：https://www.xfyun.cn/doc/spark/Web.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "359d9854-e899-40fb-b7d0-17283ca863ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "# coding: utf-8\n",
    "import _thread as thread\n",
    "import os\n",
    "import time\n",
    "import base64\n",
    "\n",
    "import base64\n",
    "import datetime\n",
    "import hashlib\n",
    "import hmac\n",
    "import json\n",
    "from urllib.parse import urlparse\n",
    "import ssl\n",
    "from datetime import datetime\n",
    "from time import mktime\n",
    "from urllib.parse import urlencode\n",
    "from wsgiref.handlers import format_date_time\n",
    "\n",
    "import websocket\n",
    "import openpyxl\n",
    "from concurrent.futures import ThreadPoolExecutor, as_completed\n",
    "import os\n",
    "\n",
    "\n",
    "class Ws_Param(object):\n",
    "    # 初始化\n",
    "    def __init__(self, APPID, APIKey, APISecret, gpt_url):\n",
    "        self.APPID = APPID\n",
    "        self.APIKey = APIKey\n",
    "        self.APISecret = APISecret\n",
    "        self.host = urlparse(gpt_url).netloc\n",
    "        self.path = urlparse(gpt_url).path\n",
    "        self.gpt_url = gpt_url\n",
    "\n",
    "    # 生成url\n",
    "    def create_url(self):\n",
    "        # 生成RFC1123格式的时间戳\n",
    "        now = datetime.now()\n",
    "        date = format_date_time(mktime(now.timetuple()))\n",
    "\n",
    "        # 拼接字符串\n",
    "        signature_origin = \"host: \" + self.host + \"\\n\"\n",
    "        signature_origin += \"date: \" + date + \"\\n\"\n",
    "        signature_origin += \"GET \" + self.path + \" HTTP/1.1\"\n",
    "\n",
    "        # 进行hmac-sha256进行加密\n",
    "        signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'),\n",
    "                                 digestmod=hashlib.sha256).digest()\n",
    "\n",
    "        signature_sha_base64 = base64.b64encode(signature_sha).decode(encoding='utf-8')\n",
    "\n",
    "        authorization_origin = f'api_key=\"{self.APIKey}\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"{signature_sha_base64}\"'\n",
    "\n",
    "        authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')\n",
    "\n",
    "        # 将请求的鉴权参数组合为字典\n",
    "        v = {\n",
    "            \"authorization\": authorization,\n",
    "            \"date\": date,\n",
    "            \"host\": self.host\n",
    "        }\n",
    "        # 拼接鉴权参数，生成url\n",
    "        url = self.gpt_url + '?' + urlencode(v)\n",
    "        # 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释，比对相同参数时生成的url与自己代码生成的url是否一致\n",
    "        return url\n",
    "\n",
    "\n",
    "\n",
    "# 收到websocket错误的处理\n",
    "def on_error(ws, error):\n",
    "    print(\"### error:\", error)\n",
    "\n",
    "\n",
    "# 收到websocket关闭的处理\n",
    "def on_close(ws):\n",
    "    print(\"### closed ###\")\n",
    "\n",
    "\n",
    "# 收到websocket连接建立的处理\n",
    "def on_open(ws):\n",
    "    thread.start_new_thread(run, (ws,))\n",
    "\n",
    "\n",
    "def run(ws, *args):\n",
    "    data = json.dumps(gen_params(appid=ws.appid, query=ws.query, domain=ws.domain))\n",
    "    ws.send(data)\n",
    "\n",
    "\n",
    "# 收到websocket消息的处理\n",
    "def on_message(ws, message):\n",
    "    # print(message)\n",
    "    data = json.loads(message)\n",
    "    code = data['header']['code']\n",
    "    if code != 0:\n",
    "        print(f'请求错误: {code}, {data}')\n",
    "        ws.close()\n",
    "    else:\n",
    "        choices = data[\"payload\"][\"choices\"]\n",
    "        status = choices[\"status\"]\n",
    "        content = choices[\"text\"][0][\"content\"]\n",
    "        print(content,end='')\n",
    "        if status == 2:\n",
    "            print(\"#### 关闭会话\")\n",
    "            ws.close()\n",
    "\n",
    "\n",
    "def gen_params(appid, query, domain):\n",
    "    \"\"\"\n",
    "    通过appid和用户的提问来生成请参数\n",
    "    \"\"\"\n",
    "\n",
    "    data = {\n",
    "        \"header\": {\n",
    "            \"app_id\": appid,\n",
    "            \"uid\": \"1234\",\n",
    "            # \"patch_id\": []    #接入微调模型，对应服务发布后的resourceid\n",
    "        },\n",
    "        \"parameter\": {\n",
    "            \"chat\": {\n",
    "                \"domain\": domain,\n",
    "                \"temperature\": 0.5,\n",
    "                \"max_tokens\": 4096,\n",
    "                \"auditing\": \"default\",\n",
    "            }\n",
    "        },\n",
    "        \"payload\": {\n",
    "            \"message\": {\n",
    "                \"text\": [{\"role\": \"user\", \"content\": query}]\n",
    "            }\n",
    "        }\n",
    "    }\n",
    "    return data\n",
    "\n",
    "\n",
    "def main(appid, api_secret, api_key, gpt_url, domain, query):\n",
    "    wsParam = Ws_Param(appid, api_key, api_secret, gpt_url)\n",
    "    websocket.enableTrace(False)\n",
    "    wsUrl = wsParam.create_url()\n",
    "\n",
    "    ws = websocket.WebSocketApp(wsUrl, on_message=on_message, on_error=on_error, on_close=on_close, on_open=on_open)\n",
    "    ws.appid = appid\n",
    "    ws.query = query\n",
    "    ws.domain = domain\n",
    "    ws.run_forever(sslopt={\"cert_reqs\": ssl.CERT_NONE})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "fbb25706-7ffc-467a-a006-4a92b2df7244",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "很高兴听到你今天很开心！如果你有任何问题或者想聊天，请随时告诉我！#### 关闭会话\n"
     ]
    }
   ],
   "source": [
    "main(\n",
    "    appid=\"XXX\",\n",
    "    api_secret=\"XXX\",\n",
    "    api_key=\"XXX\",\n",
    "    #appid、api_secret、api_key三个服务认证信息请前往开放平台控制台查看（https://console.xfyun.cn/services/bm35）\n",
    "    gpt_url=\"wss://spark-api.xf-yun.com/v3.5/chat\",\n",
    "    # Spark_url = \"ws://spark-api.xf-yun.com/v3.1/chat\"  # v3.0环境的地址\n",
    "    # Spark_url = \"ws://spark-api.xf-yun.com/v2.1/chat\"  # v2.0环境的地址\n",
    "    # Spark_url = \"ws://spark-api.xf-yun.com/v1.1/chat\"  # v1.5环境的地址\n",
    "    domain=\"generalv3.5\",\n",
    "    # domain = \"generalv3\"    # v3.0版本\n",
    "    # domain = \"generalv2\"    # v2.0版本\n",
    "    # domain = \"general\"    # v2.0版本\n",
    "    query=\"我今天很开心。\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cebd079b-0df6-4a2d-95ef-31a2e791ffa6",
   "metadata": {},
   "source": [
    "## 图片生成\n",
    "\n",
    "- 控制台：https://console.xfyun.cn/services/tti\n",
    "- API文档：https://www.xfyun.cn/doc/spark/ImageGeneration.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "8046f01b-8e71-4d8d-919f-ee37f2006747",
   "metadata": {},
   "outputs": [],
   "source": [
    "# encoding: UTF-8\n",
    "import time\n",
    "\n",
    "import requests\n",
    "from datetime import datetime\n",
    "from wsgiref.handlers import format_date_time\n",
    "from time import mktime\n",
    "import hashlib\n",
    "import base64\n",
    "import hmac\n",
    "from urllib.parse import urlencode\n",
    "import json\n",
    "from PIL import Image\n",
    "from io import BytesIO\n",
    "\n",
    "class AssembleHeaderException(Exception):\n",
    "    def __init__(self, msg):\n",
    "        self.message = msg\n",
    "\n",
    "\n",
    "class Url:\n",
    "    def __init__(this, host, path, schema):\n",
    "        this.host = host\n",
    "        this.path = path\n",
    "        this.schema = schema\n",
    "        pass\n",
    "\n",
    "\n",
    "# calculate sha256 and encode to base64\n",
    "def sha256base64(data):\n",
    "    sha256 = hashlib.sha256()\n",
    "    sha256.update(data)\n",
    "    digest = base64.b64encode(sha256.digest()).decode(encoding='utf-8')\n",
    "    return digest\n",
    "\n",
    "\n",
    "def parse_url(requset_url):\n",
    "    stidx = requset_url.index(\"://\")\n",
    "    host = requset_url[stidx + 3:]\n",
    "    schema = requset_url[:stidx + 3]\n",
    "    edidx = host.index(\"/\")\n",
    "    if edidx <= 0:\n",
    "        raise AssembleHeaderException(\"invalid request url:\" + requset_url)\n",
    "    path = host[edidx:]\n",
    "    host = host[:edidx]\n",
    "    u = Url(host, path, schema)\n",
    "    return u\n",
    "\n",
    "\n",
    "# 生成鉴权url\n",
    "def assemble_ws_auth_url(requset_url, method=\"GET\", api_key=\"\", api_secret=\"\"):\n",
    "    u = parse_url(requset_url)\n",
    "    host = u.host\n",
    "    path = u.path\n",
    "    now = datetime.now()\n",
    "    date = format_date_time(mktime(now.timetuple()))\n",
    "    # print(date)\n",
    "    # date = \"Thu, 12 Dec 2019 01:57:27 GMT\"\n",
    "    signature_origin = \"host: {}\\ndate: {}\\n{} {} HTTP/1.1\".format(host, date, method, path)\n",
    "    # print(signature_origin)\n",
    "    signature_sha = hmac.new(api_secret.encode('utf-8'), signature_origin.encode('utf-8'),\n",
    "                             digestmod=hashlib.sha256).digest()\n",
    "    signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')\n",
    "    authorization_origin = \"api_key=\\\"%s\\\", algorithm=\\\"%s\\\", headers=\\\"%s\\\", signature=\\\"%s\\\"\" % (\n",
    "        api_key, \"hmac-sha256\", \"host date request-line\", signature_sha)\n",
    "    authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')\n",
    "    # print(authorization_origin)\n",
    "    values = {\n",
    "        \"host\": host,\n",
    "        \"date\": date,\n",
    "        \"authorization\": authorization\n",
    "    }\n",
    "\n",
    "    return requset_url + \"?\" + urlencode(values)\n",
    "\n",
    "# 生成请求body体\n",
    "def getBody(appid,text):\n",
    "    body= {\n",
    "        \"header\": {\n",
    "            \"app_id\": appid,\n",
    "            \"uid\":\"123456789\"\n",
    "        },\n",
    "        \"parameter\": {\n",
    "            \"chat\": {\n",
    "                \"domain\": \"general\",\n",
    "                \"temperature\":0.5,\n",
    "                \"max_tokens\":4096\n",
    "            }\n",
    "        },\n",
    "        \"payload\": {\n",
    "            \"message\":{\n",
    "                \"text\":[\n",
    "                    {\n",
    "                        \"role\":\"user\",\n",
    "                        \"content\":text\n",
    "                    }\n",
    "                ]\n",
    "            }\n",
    "        }\n",
    "    }\n",
    "    return body\n",
    "\n",
    "# 发起请求并返回结果\n",
    "def main(text,appid,apikey,apisecret):\n",
    "    host = 'http://spark-api.cn-huabei-1.xf-yun.com/v2.1/tti'\n",
    "    url = assemble_ws_auth_url(host,method='POST',api_key=apikey,api_secret=apisecret)\n",
    "    content = getBody(appid,text)\n",
    "    print(time.time())\n",
    "    response = requests.post(url,json=content,headers={'content-type': \"application/json\"}).text\n",
    "    print(time.time())\n",
    "    return response\n",
    "\n",
    "#将base64 的图片数据存在本地\n",
    "def base64_to_image(base64_data, save_path):\n",
    "    # 解码base64数据\n",
    "    img_data = base64.b64decode(base64_data)\n",
    "\n",
    "    # 将解码后的数据转换为图片\n",
    "    img = Image.open(BytesIO(img_data))\n",
    "\n",
    "    # 保存图片到本地\n",
    "    img.save(save_path)\n",
    "\n",
    "\n",
    "# 解析并保存到指定位置\n",
    "def parser_Message(message):\n",
    "    data = json.loads(message)\n",
    "    # print(\"data\" + str(message))\n",
    "    code = data['header']['code']\n",
    "    if code != 0:\n",
    "        print(f'请求错误: {code}, {data}')\n",
    "    else:\n",
    "        text = data[\"payload\"][\"choices\"][\"text\"]\n",
    "        imageContent = text[0]\n",
    "        # if('image' == imageContent[\"content_type\"]):\n",
    "        imageBase = imageContent[\"content\"]\n",
    "        imageName = data['header']['sid']\n",
    "        savePath = f\"{imageName}.jpg\"\n",
    "        base64_to_image(imageBase,savePath)\n",
    "        print(\"图片保存路径：\" + savePath)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "83758e97-c649-44bd-ab0a-9e087b9e576d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1710491962.4746332\n",
      "1710491966.1379104\n",
      "图片保存路径：cht000b3678@dx18e41440cdeb8f3550.jpg\n"
     ]
    }
   ],
   "source": [
    "#运行前请配置以下鉴权三要素，获取途径：https://console.xfyun.cn/services/tti\n",
    "APPID ='XXX'\n",
    "APISecret = 'XXX'\n",
    "APIKEY = 'XXX'\n",
    "desc = '''生成一张图：一个男生在打篮球'''\n",
    "res = main(desc,appid=APPID,apikey=APIKEY,apisecret=APISecret)\n",
    "parser_Message(res)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2ac6be3e-14d6-4393-ba45-30ab268f26d1",
   "metadata": {},
   "source": [
    "## 图片理解\n",
    "\n",
    "- 控制台：https://console.xfyun.cn/services/image\n",
    "- API文档：https://www.xfyun.cn/doc/spark/ImageGeneration.html"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ceff20c8-5465-46cd-89e2-26acc181b5a9",
   "metadata": {},
   "source": [
    "## 语音合成\n",
    "\n",
    "- 控制台：https://console.xfyun.cn/services/medd90fec\n",
    "- API文档：https://www.xfyun.cn/doc/spark/%E8%B6%85%E6%8B%9F%E2%BC%88%E5%90%88%E6%88%90%E5%8D%8F%E8%AE%AE.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "f979cc5d-d30a-407d-9f49-e5bc7703b3e9",
   "metadata": {},
   "outputs": [],
   "source": [
    "# coding: utf-8\n",
    "import _thread as thread\n",
    "import os\n",
    "import time\n",
    "import base64\n",
    "\n",
    "import base64\n",
    "import datetime\n",
    "import hashlib\n",
    "import hmac\n",
    "import json\n",
    "from urllib.parse import urlparse\n",
    "import ssl\n",
    "from datetime import datetime\n",
    "from time import mktime\n",
    "from urllib.parse import urlencode\n",
    "from wsgiref.handlers import format_date_time\n",
    "import websocket\n",
    "import openpyxl\n",
    "from concurrent.futures import ThreadPoolExecutor, as_completed\n",
    "import os\n",
    "\n",
    "\n",
    "class Ws_Param(object):\n",
    "    # 初始化\n",
    "    def __init__(self, APPID, APIKey, APISecret, gpt_url):\n",
    "        self.APPID = APPID\n",
    "        self.APIKey = APIKey\n",
    "        self.APISecret = APISecret\n",
    "        self.host = urlparse(gpt_url).netloc\n",
    "        self.path = urlparse(gpt_url).path\n",
    "        self.gpt_url = gpt_url\n",
    "\n",
    "    # 生成url\n",
    "    def create_url(self):\n",
    "        # 生成RFC1123格式的时间戳\n",
    "        now = datetime.now()\n",
    "        date = format_date_time(mktime(now.timetuple()))\n",
    "\n",
    "        # 拼接字符串\n",
    "        signature_origin = \"host: \" + self.host + \"\\n\"\n",
    "        signature_origin += \"date: \" + date + \"\\n\"\n",
    "        signature_origin += \"GET \" + self.path + \" HTTP/1.1\"\n",
    "\n",
    "        # 进行hmac-sha256进行加密\n",
    "        signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'),\n",
    "                                 digestmod=hashlib.sha256).digest()\n",
    "\n",
    "        signature_sha_base64 = base64.b64encode(signature_sha).decode(encoding='utf-8')\n",
    "\n",
    "        authorization_origin = f'api_key=\"{self.APIKey}\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"{signature_sha_base64}\"'\n",
    "\n",
    "        authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')\n",
    "\n",
    "        # 将请求的鉴权参数组合为字典\n",
    "        v = {\n",
    "            \"authorization\": authorization,\n",
    "            \"date\": date,\n",
    "            \"host\": self.host\n",
    "        }\n",
    "        # 拼接鉴权参数，生成url\n",
    "        url = self.gpt_url + '?' + urlencode(v)\n",
    "        # 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释，比对相同参数时生成的url与自己代码生成的url是否一致\n",
    "        return url\n",
    "\n",
    "\n",
    "# 收到websocket错误的处理\n",
    "def on_error(ws, error):\n",
    "    print(\"### error:\", error)\n",
    "\n",
    "\n",
    "# 收到websocket关闭的处理\n",
    "def on_close(ws):\n",
    "    print(\"### closed ###\")\n",
    "\n",
    "\n",
    "# 收到websocket连接建立的处理\n",
    "def on_open(ws):\n",
    "    thread.start_new_thread(run, (ws,))\n",
    "\n",
    "\n",
    "# 收到websocket消息的处理\n",
    "def on_message(ws, message):\n",
    "    message = json.loads(message)\n",
    "    code = message['header']['code']\n",
    "    if code != 0:\n",
    "        print(\"### 请求出错： \", message)\n",
    "    else:\n",
    "        payload = message.get(\"payload\")\n",
    "        status = message['header']['status']\n",
    "        if status == 2:\n",
    "            print(\"### 合成完毕\")\n",
    "            ws.close()\n",
    "        if payload and payload != \"null\":\n",
    "            audio = payload.get(\"audio\")\n",
    "            if audio:\n",
    "                audio = audio[\"audio\"]\n",
    "                with open(fr'./{ws.vcn}.mp3', 'ab') as f:\n",
    "                    f.write(base64.b64decode(audio))\n",
    "\n",
    "\n",
    "def run(ws, *args):\n",
    "    body = {\n",
    "        \"header\": {\n",
    "            \"app_id\": ws.appid,\n",
    "            \"status\": 0\n",
    "        },\n",
    "        \"parameter\": {\n",
    "            \"oral\": {\n",
    "                \"spark_assist\": 1,\n",
    "                \"oral_level\": \"mid\"\n",
    "            },\n",
    "            \"tts\": {\n",
    "                \"vcn\": ws.vcn,\n",
    "                \"speed\": 50,\n",
    "                \"volume\": 50,\n",
    "                \"pitch\": 50,\n",
    "                \"bgs\": 0,\n",
    "                \"reg\": 0,\n",
    "                \"rdn\": 0,\n",
    "                \"rhy\": 0,\n",
    "                \"scn\": 0,\n",
    "                \"version\": 0,\n",
    "                \"L5SilLen\": 0,\n",
    "                \"ParagraphSilLen\": 0,\n",
    "                \"audio\": {\n",
    "                    \"encoding\": \"lame\",\n",
    "                    \"sample_rate\": 16000,\n",
    "                    \"channels\": 1,\n",
    "                    \"bit_depth\": 16,\n",
    "                    \"frame_size\": 0\n",
    "                },\n",
    "                \"pybuf\": {\n",
    "                    \"encoding\": \"utf8\",\n",
    "                    \"compress\": \"raw\",\n",
    "                    \"format\": \"plain\"\n",
    "                }\n",
    "            }\n",
    "        },\n",
    "        \"payload\": {\n",
    "            \"text\": {\n",
    "                \"encoding\": \"utf8\",\n",
    "                \"compress\": \"raw\",\n",
    "                \"format\": \"json\",\n",
    "                \"status\": 0,\n",
    "                \"seq\": 0,\n",
    "                \"text\": str(base64.b64encode(ws.text.encode('utf-8')), \"UTF8\")\n",
    "            }\n",
    "        }\n",
    "    }\n",
    "\n",
    "    ws.send(json.dumps(body))\n",
    "\n",
    "\n",
    "def main(appid, api_secret, api_key, url, text, vcn):\n",
    "    wsParam = Ws_Param(appid, api_key, api_secret, url)\n",
    "    wsUrl = wsParam.create_url()\n",
    "    ws = websocket.WebSocketApp(wsUrl, on_message=on_message, on_error=on_error, on_close=on_close, on_open=on_open)\n",
    "    websocket.enableTrace(False)\n",
    "    ws.appid = appid\n",
    "    ws.text = text\n",
    "    ws.vcn = vcn\n",
    "    ws.run_forever(sslopt={\"cert_reqs\": ssl.CERT_NONE})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "f4818028-563f-412d-ac42-66d9b7dfc4fe",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "### 合成完毕\n"
     ]
    }
   ],
   "source": [
    "main(\n",
    "    appid=\"XXX\",\n",
    "    api_secret=\"XXX\",\n",
    "    api_key=\"XXX\",\n",
    "    url=\"wss://cbm01.cn-huabei-1.xf-yun.com/v1/private/medd90fec\",\n",
    "    # 待合成文本\n",
    "    text=\"今天天气很不错，适合打羽毛球。\",\n",
    "    # 发音人参数\n",
    "    vcn = \"x4_lingfeizhe_oral\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23828570-1fcd-403b-a729-16d2b8c19ab1",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3.10"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
